// ============================================================================
// FileName: DatabaseLogger.cs
//
// Description:
// Logs events from the StatefulProxy to a database.
//
// Author(s):
// Aaron Clauson
//
// History:
// 12 Nov 2006	Aaron Clauson	Created.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2006 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
// ============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using CN.Sys;
using CN.VoIP.SIPServer;
using log4net;

namespace CNSIPSwitch
{
    public class DatabaseLogger
    {
        private const int PROXYEVENT_QUEUE_MAXSIZE = 100;
        
        private static ILog logger = log4net.LogManager.GetLogger("sipproxy");

        private StorageLayer m_logStorageLayer = null;

        private static Queue m_proxyLogEventQueue = new Queue();		                         // Queues events to be written to database.
        private static ManualResetEvent m_proxyLogEventWaiting = new ManualResetEvent(false);    // Fired to indicate there is a new log event in the queue.

        public bool LogEvents = true;   // Set to false to stop logging events to database.

        public DatabaseLogger(StorageTypes storageType, string dbConnStr)
        {
            try
            {
                logger.Debug("DatabaseLogger started.");

                m_logStorageLayer = new StorageLayer(storageType, dbConnStr);

                Thread dbLoggerThread = new Thread(new ThreadStart(ProcessLogEventQueue));
                dbLoggerThread.Name = "dblogger-thread";
                dbLoggerThread.Start();
            }
            catch (Exception excp)
            {
                logger.Error("Exception DatabaseLogger. " + excp.Message);
            }
        }

        /// <summary>
        /// Used to allow the owning application to add a new event into the queue to be written to the database.
        /// </summary>
        public void AddEvent(ProxyMonitorEvent proxyEvent)
        {
            try
            {
                lock (m_proxyLogEventQueue.SyncRoot)
                {
                    if (m_proxyLogEventQueue.Count > PROXYEVENT_QUEUE_MAXSIZE)
                    {
                        logger.Error("The proxy log queue has exceeded the threshold " + PROXYEVENT_QUEUE_MAXSIZE + " and is being dumped.");
                        m_proxyLogEventQueue.Clear();

                    }

                    m_proxyLogEventQueue.Enqueue(proxyEvent);
                    m_proxyLogEventWaiting.Set();
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception AddEvent. " + excp.Message);
            }
        }
                
        /// <summary>
        /// 
        /// </summary>
        private void ProcessLogEventQueue()
        {
            try
            {
                while (LogEvents)
                {
                    lock (m_proxyLogEventQueue.SyncRoot)
                    {
                        DateTime dbLogStartTime = DateTime.Now;
                        string insertSQL = null;
                        int eventCount = 0;

                        while (m_proxyLogEventQueue.Count > 0)
                        {
                            ProxyMonitorEvent proxyLogEvent = (ProxyMonitorEvent)m_proxyLogEventQueue.Dequeue();

                            insertSQL += "insert into switchlog (logid, username, logmessage, inserted) values " +
                                "(default,'" + proxyLogEvent.Username + "','" + proxyLogEvent.Message.Replace("'", "''") + "', default);";

                            eventCount++;

                        }

                        try
                        {
                            if (insertSQL != null)
                            {
                                m_logStorageLayer.ExecuteNonQuery(insertSQL);

                                TimeSpan dbLogTime = DateTime.Now.Subtract(dbLogStartTime);

                                logger.Debug("Inserted " + eventCount + " events into the database in " + dbLogTime.TotalMilliseconds + "ms.");
                            }
                        }
                        catch (Exception dbLogExcp)
                        {
                            logger.Error("Exception ProcessingLogEvent. " + dbLogExcp.Message);
                        }
                    }

                    m_proxyLogEventWaiting.Reset();
                    m_proxyLogEventWaiting.WaitOne();
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ProcessLogEventQueue, thread stopped. " + excp.Message);
            }
        }
    }
}
